/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.utx.spot.timesync;

import com.sun.spot.peripheral.IAT91_TC;
import com.sun.spot.peripheral.TimerCounterBits;

/**
 *
 */
class AT91TimerCounterThread extends Thread {

    private IAT91_TC clockTimer;
    private IAT91_TC testTimer;

    private long microseconds;
    private int usecsPerIrq;
    private int ticksPerIrq;
	private int regC;
    private long timestamp;

    private final Object monitor = new Object();
	
    public AT91TimerCounterThread(IAT91_TC clockTimer, IAT91_TC testTimer, int usecsPerIrq, int ticksPerIrq) {
        this.clockTimer = clockTimer;
        this.testTimer = testTimer;
        this.usecsPerIrq = usecsPerIrq;
        this.ticksPerIrq = ticksPerIrq;
		this.regC = ticksPerIrq;
        this.timestamp = 0;
    }

    public void run() {
        while (true) {
            
            clockTimer.enableIrq(
                    TimerCounterBits.TC_CPCS |  // counter reaches reg C
                    TimerCounterBits.TC_LDRAS); // reg A is loaded (capture)
            clockTimer.waitForIrq();
            handleIrq(clockTimer.status());
        }
    }

    public long currentTimeMicros() {
        long usecs;
        int counter;
        synchronized(monitor) {
            usecs = microseconds;
            counter = clockTimer.counter();
        }
        return usecs + ((counter * usecsPerIrq) / ticksPerIrq);
    }
   
	public void setClockOffset(long offset) {
		synchronized (monitor) {
			microseconds += offset;
		}
	}
	
    public void setTicksPerIrq(int ticksPerIrq) {
        this.ticksPerIrq = ticksPerIrq;
    }

    public long lastTimestamp() {
        synchronized(monitor) {
            final long ts = timestamp;
            timestamp = 0;
            return ts;
        }
    }
	
	public boolean isSfdHigh() {
		//
		// To check the SFD pin we use the TIOA mirror bit in the status register.
		// However, reading the status register will clear pending interrupt status
		// To prevent losing an interrupt status, use the handleIrq function before
		// checking the mirror bit.
		//
		return (handleIrq(clockTimer.status()) & TimerCounterBits.TC_MTIOA) != 0;
	}
	
	int handleIrq(int status) {
		synchronized(monitor) {
			
			// RC compare interrupt - update microsecond count
			if ((status & TimerCounterBits.TC_CPCS) != 0) {
				microseconds += usecsPerIrq;
				
				// update interrupt rate
				if(regC != ticksPerIrq) {
					clockTimer.setRegC(regC);
					testTimer.setRegC(regC);
					regC = ticksPerIrq;
				}
			}
			
			// RA load register - read counter timestamp only if there
			// not an overrun condition.
			if ((status & TimerCounterBits.TC_LDRAS) != 0) {
				// read register A even if there is a overrun to clear the error condition
				int regA = clockTimer.regA();
				
				if ((status & TimerCounterBits.TC_LOVRS) == 0) {
					// no overrun, go ahead and get the timestamp
					long usecs = microseconds;
				
					// If there was also a RC compare interrupt, check if
					// the capture event occurred before or after the
					// counter reset.
					if ((status & TimerCounterBits.TC_CPCS) != 0 && (regA > ticksPerIrq/2)) {
						usecs -= usecsPerIrq;
					}
				
					timestamp = usecs + ((regA * usecsPerIrq) / ticksPerIrq);
				}
			}
		}		
		return status;
	}
}
